package io.kiki.sba.registry.server.data_store.remoting.dataserver.handler;

import io.kiki.sba.registry.api.Channel;
import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.Node;
import io.kiki.sba.registry.common.model.dataserver.DatumDigest;
import io.kiki.sba.registry.common.model.slot.DataSlotDiffDigestRequest;
import io.kiki.sba.registry.common.model.slot.DataSlotDiffDigestResult;
import io.kiki.sba.registry.common.model.slot.DataSlotDiffUtils;
import io.kiki.sba.registry.common.model.slot.filter.SyncSlotAcceptorManager;
import io.kiki.sba.registry.common.model.store.Publisher;
import io.kiki.sba.registry.server.data_store.bootstrap.ServerConfig;
import io.kiki.sba.registry.server.data_store.cache.DatumStorageDelegate;
import io.kiki.sba.registry.server.data_store.slot.SlotManager;
import io.kiki.sba.registry.server.shared.remoting.AbstractServerHandler;
import io.kiki.sba.registry.util.StringFormatter;
import io.kiki.sba.registry.util.StringUtil;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.Map;


public abstract class BaseSlotDiffDigestRequestHandler extends AbstractServerHandler<DataSlotDiffDigestRequest> {

    private static final Logger logger = LoggerFactory.getLogger(BaseSlotDiffDigestRequestHandler.class);
    @Getter
    @Autowired
    protected SlotManager slotManager;
    @Autowired
    protected ServerConfig serverConfig;
    @Getter
    @Resource
    private DatumStorageDelegate datumStorageDelegate;

    public BaseSlotDiffDigestRequestHandler() {

    }

    protected abstract boolean preCheck(DataSlotDiffDigestRequest request);

    protected abstract boolean postCheck(DataSlotDiffDigestRequest request);

    @Override
    public Object doHandle(Channel channel, DataSlotDiffDigestRequest request) {
        try {
            final int slotId = request.getSlotId();
            if (!preCheck(request)) {
                return new GenericResponse().fillFailed("pre check fail of: " + slotId);
            }
            slotManager.triggerUpdateSlots(request.getSlotsEpoch());

            // not use acceptorManager to filter in getPublishers() method,
            // because getPublishers() method only loop dataIndoId, will not loop publishers;
            Map<String, Map<String, Publisher>> existingPublishers = datumStorageDelegate.getDataInfoIdToRegisterIdToPublisherMapMap(serverConfig.getDataCenter(), request.getSlotId());

            // use acceptorManager in DataSlotDiffUtils.diffDigestResult,
            // as it will loop publishers once
            DataSlotDiffDigestResult result = calcDiffResult(request.getLocalDataCenter(), slotId, request.getDatumDigest(), existingPublishers, request.getAcceptorManager());
            result.setSlotsEpoch(slotManager.getSlotsEpoch());

            if (!postCheck(request)) {
                return new GenericResponse().fillFailed("post check fail of: " + slotId);
            }
            return new GenericResponse().fillSucceed(result);
        } catch (Throwable e) {
            String msg = StringFormatter.format("DiffSyncDigest request from {} error for slot {},{}", request.getLocalDataCenter(), request.getLocalDataCenter(), request.getSlotId());
            logger.error(msg, e);
            return new GenericResponse().fillFailed(msg);
        }
    }

    private DataSlotDiffDigestResult calcDiffResult(String requestDataCenter, int targetSlot, Map<String, DatumDigest> targetDigestMap, Map<String, Map<String, Publisher>> existingPublishers, SyncSlotAcceptorManager syncSlotAcceptorManager) {
        DataSlotDiffDigestResult result = DataSlotDiffUtils.diffDigestResult(targetDigestMap, existingPublishers, syncSlotAcceptorManager);
        DataSlotDiffUtils.logDiffResult(requestDataCenter, result, targetSlot);
        return result;
    }

    @Override
    protected Node.NodeType getConnectNodeType() {
        return Node.NodeType.data_store;
    }

    @Override
    public Class interest() {
        return DataSlotDiffDigestRequest.class;
    }

    @Override
    public void checkParam(DataSlotDiffDigestRequest request) {
        StringUtil.checkNonNegative(request.getSlotId(), "request.slotId");
        StringUtil.nullValue(request.getDatumDigest(), "request.datumDigest");
    }

    @Override
    public Object buildFailedResponse(String msg) {
        return new GenericResponse().fillFailed(msg);
    }


}
